using System.Collections.Generic;
using UnityEngine;

namespace Utilities
{
    /// <summary>
    /// 分离轴算法（简称SAT）通常用于检查两个简单多边形（凸边形）之间或多边形与圆之间的碰撞。
    /// 本质上，如果您能够绘制一条线来分隔两个多边形，则它们不会发生碰撞，如果找不到一条线来分割两个多边形，则它们发生碰撞。
    /// </summary>
    public class Sat
    {
        public class Polygon
        {
            public List<Vector2> Points = new List<Vector2>();
            public List<Vector2> Edges = new List<Vector2>();

            public static Polygon Create(List<Vector3> points)
            {
                Polygon polygon = new Polygon();
                polygon.Points.Clear();
                foreach (var v3 in points)
                {
                    polygon.Points.Add(new Vector2(v3.x, v3.z));
                }

                return Create(polygon);
            }

            private static Polygon Create(Polygon polygon)
            {
                if (polygon.Points != null)
                {
                    for (int i = 0; i < polygon.Points.Count; i++)
                    {
                        var j = i + 1;
                        if (j < polygon.Points.Count)
                        {
                            polygon.Edges.Add(polygon.Points[j] - polygon.Points[i]);
                        }
                    }

                    polygon.Edges.Add(polygon.Points[0] - polygon.Points[polygon.Points.Count - 1]);
                }

                return polygon;
            }
        }

        public static bool CollisionDetection(Polygon polygonA, Polygon polygonB)
        {
            int edgeCountA = polygonA.Edges.Count;
            int edgeCountB = polygonB.Edges.Count;
            Vector2 edge;

            // 循环遍历两个多边形的所有边
            for (int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++)
            {
                if (edgeIndex < edgeCountA)
                {
                    edge = polygonA.Edges[edgeIndex];
                }
                else
                {
                    edge = polygonB.Edges[edgeIndex - edgeCountA];
                }

                //查找多边形当前是否相交

                // 找出垂直于当前边的轴
                Vector2 axis = new Vector2(-edge.y, edge.x);
                axis.Normalize();

                // 求多边形在当前轴上的投影
                float minA = 0;
                float minB = 0;
                float maxA = 0;
                float maxB = 0;
                ProjectPolygon(axis, polygonA, ref minA, ref maxA);
                ProjectPolygon(axis, polygonB, ref minB, ref maxB);

                // 检查多边形投影当前是否相交
                if (IntervalDistance(minA, maxA, minB, maxB) > 0)
                    return false;
            }

            return true;
        }

        private static void ProjectPolygon(Vector2 axis, Polygon polygon, ref float min, ref float max)
        {
            //要在轴上投影一个点，使用点积
            float dotProduct = Vector2.Dot(axis, polygon.Points[0]);
            min = dotProduct;
            max = dotProduct;
            for (int i = 0; i < polygon.Points.Count; i++)
            {
                dotProduct = Vector2.Dot(polygon.Points[i], axis);
                if (dotProduct < min)
                {
                    min = dotProduct;
                }
                else
                {
                    if (dotProduct > max)
                    {
                        max = dotProduct;
                    }
                }
            }
        }

        private static float IntervalDistance(float minA, float maxA, float minB, float maxB)
        {
            if (minA < minB)
            {
                return minB - maxA;
            }
            else
            {
                return minA - maxB;
            }
        }
    }
}